<?php
/**
 * Created by PhpStorm.
 * User: longli
 * VX: isa1589518286
 * Date: 2021/08/23
 * Time: 23:16
 * @link http://www.lmterp.cn
 */

namespace app\common\service\logistics;


use app\common\library\Tools;
use app\common\model\Channel;
use app\common\model\ChannelAttr;
use app\common\model\ChannelCountrySettings;
use app\common\model\OrdersRules;
use app\common\model\OrdersRulesGroup;
use app\common\model\Orders;
use app\common\model\Product;
use app\common\model\ProductStore;
use app\common\service\BaseService;
use app\common\service\product\ProductService;
use think\facade\Config;
use think\facade\Log;

/**
 * 选择渠道服务类
 * Class ChannelOrderService
 * @package app\common\service\logistics
 */
class ChannelOrderService extends BaseService
{

    /**
     * 订单
     * @var Orders
     */
    private $order;

    /**
     * 通过订单计算运费
     * @param Orders|int $order 订单或者订单id
     * @return array
     * @date 2021/08/24
     * @author longli
     */
    public function calculateByOrder($order)
    {
        $this->order = Orders::getObj($order);
        if(empty($this->order)) return [];
        $rule = new \app\common\service\orders\ParseRuleService(OrdersRulesGroup::REF_TYPE_LGS, $this->order);
        $channel = $rule->getIds();
        return $this->calculate($this->order->buyer_country_code, $this->order->detail->toArray(), $channel['include'], $channel['exclude']);
    }

    /**
     * 计算运费
     * @param string $countryCode 国家二字简码
     * @param array $skus 产品SKU
     * @param array $inId 指定可走渠道id
     * @param array $notId 指定不可走渠道id
     * @return array|void
     * @date 2021/08/23
     * @author longli
     * @example
     * $skus = [
     *     [
     *        "sku" => "A001",
     *        "qty" => 2,
     *    ]
     * ];
     */
    public function calculate($countryCode, $skus, $inId = [], $notId = [])
    {
        $orderMsg = !empty($this->order) ? "，订单号为：【{$this->order->order_sn}】" : "";
        $maxLength = ProductStore::getMaxLength(array_column($skus, 'sku')); // 最长边
        $totalWeight = !empty($this->order)
            ? $this->order->getWeight()
            : ProductService::getInstance()->getTotalWeight($skus); // 总重量
        $settings = $this->getChannels($countryCode, $totalWeight, $maxLength, $inId, $notId);
        if($settings->isEmpty())
        {
            Log::info("暂无渠道可走当前国家代码为【{$countryCode}】{$orderMsg}");
            return; // 无渠道
        }

        // 过虑国家邮编和物流属性
        $postCode = !empty($this->order) ? [$this->order->buyer_post_code] : [];
        $settings = $settings->filter(function($setting) use($skus, $postCode)
        {
            if(!$this->filterCountryAttr($setting, $skus, $postCode)) return false;
            if(!$this->filterAttr($setting, $skus)) return false;
            return true;
        });
        if($settings->isEmpty())
        {
            Log::info("过虑渠道可走属性后，暂无渠道可走当前国家代码为【{$countryCode}】{$orderMsg}");
            return; // 无渠道
        }

        $quotation = $this->getQuotation($settings, $totalWeight);
        if(empty($quotation))
        {
            Log::info("渠道暂无报价单，国家代码为【{$countryCode}】，重量为【{$totalWeight}】，最长边为【{$maxLength}】{$orderMsg}");
            return; // 无报价单
        }
        return $quotation;
    }

    /**
     * 通过重量来计算运费
     * @param string $countryCode 国家二字简码
     * @param float $weight 重量
     * @param array|string 指定渠道id
     * @return array|void
     * @date 2021/08/23
     * @author longli
     */
    public function calculateByWeight($countryCode, $weight, $cId = [])
    {
        $settings = $this->getChannels($countryCode, $weight, 0, $cId);
        if($settings->isEmpty())
        {
            Log::info("暂无渠道可走当前国家代码为【{$countryCode}】，重量为【{$weight}】");
            return; // 无渠道
        }
        $quotation = $this->getQuotation($settings, $weight);
        if(empty($quotation))
        {
            Log::info("渠道暂无报价单，国家代码为【{$countryCode}】，重量为【{$weight}】");
            return; // 无报价单
        }
        return $quotation;
    }

    /**
     * 获取渠道
     * @param string $countryCode 国家二字简码
     * @param float $totalWeight 总重量
     * @param float $maxLength 最长边
     * @param array $inId 指定可走渠道id
     * @param array $notId 指定不可走渠道id
     * @return ChannelCountrySettings[]
     * @date 2021/08/23
     * @author longli
     */
    private function getChannels($countryCode, $totalWeight = 0, $maxLength = 0, $inId = [], $notId = [])
    {
        // 获取可到达当前国家的渠道
        $where = [
            ["country_code", "eq", trim($countryCode)],
            ["status", "eq", ChannelCountrySettings::IS_YES]
        ];
        if(!empty($inId)) $where[] = ["channel_id", "IN", $inId];
        if(!empty($notId)) $where[] = ["channel_id", "NOT IN", $notId];
        return ChannelCountrySettings::with([
            "channel" => function($query)
            {
                $query->where("status", ChannelCountrySettings::IS_YES)->with(["attrs" => function($query)
                {
                    $query->where("status", ChannelCountrySettings::IS_YES);
                }]);
            },
            "quotation" => function($query) use($totalWeight, $maxLength)
            {
                $where = [
                    ["status", "eq", Channel::IS_YES],
                    ["start_weight", "elt", $totalWeight],
                ];
                if($totalWeight > 0) $where[] = ["end_weight", "egt", $totalWeight];
                if($maxLength > 0) $where[] = ["max_length", "egt", $maxLength];
                $query->where($where);
            }
        ])->where($where)
        ->select()->filter(function($setting)
        {
            return !(empty($setting->channel) || empty($setting->quotation));
        });
    }

    /**
     * 获取渠道报价单
     * @param ChannelCountrySettings[] $settings 国家渠道配置
     * @param float $totalWeight 重量
     * @return array|void
     * @date 2021/08/25
     * @author longli
     */
    private function getQuotation($settings, $totalWeight)
    {
        // 计算权重值进行排序
        $weightPrice = Config::get('param.logistics_weight_price', 1); // 价格权重
        $weightSpeed = Config::get('param.logistics_weight_speed', 1); // 时效权重
        $qList = [];
        /*$showFiled = ['start_weight', 'end_weight', 'max_length', 'price', 'register_price', 'operation_price',
            'fuel_price', 'customs_price', 'backend_price', 'tax_price', 'other_price', 'paobi', 'discount'];*/
        foreach($settings as $setting)
        {
            foreach($setting->quotation as $quotation)
            {
                $totalPrice = $quotation->getTotalPrice($totalWeight);
                $speed = $setting->getTimerSpeed();
                // 计算权重
                $sort = $totalPrice * $weightPrice;
                if($speed > 0) $sort += $speed * $weightSpeed;
                // $info = Tools::visibleArray($showFiled, $quotation->toArray()); // 费用明细
                $qList[] = [
                    "channel_id"   => $setting->channel_id, // 渠道 id
                    "country_code" => $setting->country_code, // 国家
                    "channel_name" => $setting->channel->channel_name, // 渠道名称
                    "channel_type" => $setting->channel->channel_type, // 渠道类型
                    "success_rate" => $setting->success_rate, // 妥投率
                    "total_price"  => $totalPrice, // 总运费
                    "total_weight" => $totalWeight, // 总重量
                    "speed"        => abs($speed), // 时效天数
                    "sort"         => $sort, // 权重，从小到大排序
                    "info"         => $quotation->getPirceInfo($totalWeight),
                ];
            }
        }
        if(count($qList) < 2) return $qList;
        return Tools::sortArrayByManyField($qList, 'sort', SORT_ASC, 'total_price', SORT_ASC, 'speed', SORT_ASC, 'success_rate', SORT_DESC);
    }

    /**
     * 过虑国家渠道不可走属性和邮编
     * @param ChannelCountrySettings $setting 国家渠道配置
     * @param array $skus 产品SKU
     * @param array $postCode 邮编
     * @return bool 渠道配置
     * @date 2021/08/23
     * @author longli
     */
    private function filterCountryAttr($setting, $skus, $postCode = [])
    {
        $skus = array_column($skus, 'sku');
        // 过滤渠道单日最大发货量
        if(!empty($this->order)
            && $setting->channel->day_max_qty
            && $setting->channel->today_qty >= $setting->channel->day_max_qty) return false;

        // 过虑邮编
        if(!empty($postCode) && !empty($setting->exclude_post_code))
        {
            $cCode = explode(',', $setting->exclude_post_code);
            if(array_intersect($cCode, $postCode)) return false;
        }

        // 过虑物流属性
        if(!empty($setting->exclude_attr))
        {
            $cAttr = explode(',', $setting->exclude_attr);
            $attrs = ChannelAttr::getCattrBySku($skus);
            if(array_intersect($cAttr, $attrs)) return false;
        }
        return true;
    }

    /**
     * 过虑渠道可走和不可走属性
     * @param ChannelCountrySettings $setting 国家渠道配置
     * @param array $skus 产品SKU
     * @return bool
     * @date 2021/08/23
     * @author longli
     */
    private function filterAttr($setting, $skus)
    {
        $skus = array_column($skus, 'sku');
        foreach($setting->channel->attrs as $allow)
        {
            // 包含 SKU
            if(!empty($allow->include_sku))
            {
                $iSku = explode(',', $allow->include_sku);
                if(!Tools::containArray($iSku, $skus)) return false;
            }
            // 不包含 SKU
            if(!empty($allow->exlude_sku))
            {
                $eSku = explode(',', $allow->exlude_sku);
                if(array_intersect($eSku, $skus)) return false;
            }
            // 包含物流属性
            if(!empty($allow->include_attr))
            {
                $iAttr = explode(',', $allow->include_attr);
                $attrs = ChannelAttr::getCattrBySku($skus);
                if(empty($attrs) || !Tools::containArray($iAttr, $attrs)) return false;
            }
            // 不包含物流属性
            if(!empty($allow->exlude_attr))
            {
                $iAttr = explode(',', $allow->exlude_attr);
                $attrs = ChannelAttr::getCattrBySku($skus);
                if(array_intersect($iAttr, $attrs)) return false;
            }
        }
        return true;
    }

    /**
     * 拆包试算运费
     * @date 2021/09/02
     * @author longli
     */
    public function splitPackage()
    {
        // @todo 当有多个商品时，计算拆包发送是否更划算
    }
}